home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************
- * file: GUI.C
- * purpose: simple windowing interface based on the Zortech flash graphics library
- * contains:
- * gui_open(void); must be called before using gui
- * gui_close(void); called after finished with gui
- * object_add(short id,void (*message_handler)(MESSAGE *message,void *data),void *data,fg_pbox_t screen); add object to active list
- * object_remove(void *data); removes object from active list
- * object_exists(void *data); returns pointer to object or NULL if none
- * message_get(MESSAGE *message); gets next message from input devices or system
- * message_send(MESSAGE *message); send a message down the list
- * message_send_object(MESSAGE *message,void *data); send a message to a particular object
- * message_box(char *str); puts a message box on the screen
- * error_box(short id); displays a box for errors
- * yn_box(char *str); displays a message box and waits for y/n response
- * exclusive_focus_set(void *data); sets focus to a particular object
- * exclusive_focus_clear(void *data); clears focus restriction of an object
- * input_handler_set_default(void (*message_handler)(MESSAGE *message)); message handler to be invoked when no other objects want message
- * screen_clear(void); clears screen to system background color
- * system: Written for the flash graphics library in Zortech 3.0
- * There is an MSDOS dependency in _bios_keybrd()
- * copyright: 1991 by David Weber. All rights reserved.
- * This software can be used for any purpose as object, library or executable.
- * It cannot be sold for profit as source code.
- * history:
- * 12-17-91 - initial code
- * 01-31-93 - this code is now obsolete, see the CPP gui package
- **************************************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <bios.h>
- #include <ctype.h>
- #include <signal.h>
- #include <cerror.h>
- #define GUI_SOURCE
- #include "gui.h"
-
- /* sizes of things */
- #define MESSAGE_QUEUE_SIZE 64 /* depth of message queue before messages get lost */
- #define EXCLUSIVE_FOCUS_STACK_SIZE 16 /* number of exclusive focus requests that can be stacked */
-
- /* global data */
- short gui_errno; /* errno for gui, last error or 0 if OK */
- short gui_screen_width,gui_screen_height; /* gui screen dimensions */
- short gui_char_width,gui_char_height; /* gui text cell dimensions */
-
- /* local data */
- /* objects */
- static GOB first_object = {OBJECT_NULL,NULL,NULL,-1,-1,-1,-1,NULL};
- static GOB *exclusive_focus[EXCLUSIVE_FOCUS_STACK_SIZE]; /* for restricting focus to a specific object */
- static short exclusive_focus_count = 0;
- static void (*default_input_handler)(MESSAGE *message) = NULL; /* message handler when no other objects want the input message */
- /* mouse */
- static unsigned short last_mouse_state = 0; /* mouse buttons */
- static short last_mouse_x,last_mouse_y; /* mouse position */
- /* message queue */
- static MESSAGE message_queue[MESSAGE_QUEUE_SIZE]; /* circular message queue and pointers */
- static short message_queue_head,message_queue_tail;
-
-
- /* local prototypes */
- static short scan_object_list(MESSAGE *message);/* pass current message to all objects */
- static short getkey(void); /* get a key from the keyboard */
- static short checkey(void); /* see if there is a key at the keyboard */
- static short wait_key_or_mouse_click(short *x,short *y); /* wait until the user presses a key or clicks the mouse */
- static void message_box_handler(MESSAGE *message,void *data); /* handles message box messages */
- static short message_box_core(char *str,short yn); /* core of message box display */
- static int _far critical_error_handler(int *ax,int *di); /* handle critical errors */
-
-
-
- /************************************************
- * function: short gui_open(void)
- * This function must be called before using the gui
- * parameters: none
- * returns: 1 if OK or 0 if cannot open
- ************************************************/
- short gui_open(void)
- {
- MESSAGE message;
-
- if (fg_init() == FG_NULL) /* initialize */
- return 0;
- if (signal(SIGTERM,SIG_IGN) == SIG_ERR) /* turn off ^C terminate */
- return 0;
- #ifndef DEBUG
- _cerror_handler = critical_error_handler;
- cerror_open(); /* turn on critical error redirection */
- #endif
- if (fg.nsimulcolor < 16) /* if monochrome then map colors appropriately */
- {
- /* black */
- fg_setcolornum(FG_BLUE,FG_BLACK);
- fg_setcolornum(FG_GREEN,FG_BLACK);
- fg_setcolornum(FG_CYAN,FG_BLACK);
- fg_setcolornum(FG_RED,FG_BLACK);
- fg_setcolornum(FG_MAGENTA,FG_BLACK);
- fg_setcolornum(FG_BROWN,FG_BLACK);
- fg_setcolornum(FG_GRAY,FG_BLACK);
- /* white */
- fg_setcolornum(FG_LIGHT_BLUE,FG_WHITE);
- fg_setcolornum(FG_LIGHT_GREEN,FG_WHITE);
- fg_setcolornum(FG_LIGHT_CYAN,FG_WHITE);
- fg_setcolornum(FG_LIGHT_RED,FG_WHITE);
- fg_setcolornum(FG_LIGHT_MAGENTA,FG_WHITE);
- fg_setcolornum(FG_YELLOW,FG_WHITE);
- fg_setcolornum(FG_LIGHT_WHITE,FG_WHITE);
- }
- first_object.next = NULL; /* initialize object list */
- message_queue_head = message_queue_tail = 0; /* initialize message queue */
- exclusive_focus_count = 0; /* initialize exclusive focus stack */
- gui_errno = 0; /* initialize gui error */
- gui_screen_width = fg_box_width(fg.displaybox); /* set up gui dimensions */
- gui_screen_height = fg_box_height(fg.displaybox);
- gui_char_width = fg_box_width(fg.charbox);
- gui_char_height = fg_box_height(fg.charbox);
- if (fg.msm)
- {
- fg_msm_setcurpos((fg.displaybox[FG_X2]-fg.displaybox[FG_X1])/2+fg.displaybox[FG_X1],(fg.displaybox[FG_Y2]-fg.displaybox[FG_Y1])/2+fg.displaybox[FG_Y1]);
- fg_msm_showcursor();
- fg_flush();
- last_mouse_state = fg_msm_getstatus((fg_coord_t *)&last_mouse_x,(fg_coord_t *)&last_mouse_y);
- }
- message.id = M_START; /* send start message */
- message_send(&message);
- screen_clear();
- return 1;
- }
-
-
- /************************************************
- * function: void gui_close(void)
- * must be called after finished with gui
- * parameters: none
- * returns: none
- ************************************************/
- void gui_close(void)
- {
- GOB *o,*o2;
-
- o = first_object.next; /* free all objects */
- while (o != NULL)
- {
- o2 = o;
- o = o->next;
- fg_free_handle(o2->save_area);
- free(o2);
- }
- fg_msm_hidecursor();
- #ifndef DEBUG
- cerror_close(); /* turn off critical error redirection */
- #endif
- fg_term(); /* return to text mode */
- }
-
-
- /************************************************
- * function: short object_add(short id,void (*message_handler)(MESSAGE *message,void *data),void *data,fg_pbox_t screen)
- * add object to active list
- * hide the cursor before calling this and restore it afterwards
- * parameters: id for object, function pointer to message handler for object,
- * pointer to data for object, dimensions of object
- * returns: 1 if OK or 0 if failed
- * note: if the message handler for object wishes to eat the message it should
- * change to message to M_NONE
- ************************************************/
- short object_add(short id,void (*message_handler)(MESSAGE *message,void *data),void *data,fg_pbox_t screen)
- {
- GOB *o;
- MESSAGE error;
-
- if ((o = (GOB *) malloc(sizeof (GOB))) == NULL) /* allocate object */
- {
- error.id = gui_errno = M_NOMEM;
- error.data.ptr_data = object_add;
- message_send(&error);
- return 0;
- }
- o->next = NULL;
- o->id = id;
- o->message_handler = message_handler;
- o->data = data;
- fg_box_cpy(o->screen,screen);
- fg_boxclip(fg.displaybox,o->screen,o->screen);
- if ((o->save_area = fg_save(o->screen)) == NULL)
- {
- free(o);
- error.id = gui_errno = M_NOMEM;
- error.data.ptr_data = object_add;
- message_send(&error);
- return 0;
- }
- o->next = first_object.next; /* add object to list */
- first_object.next = o;
- return 1;
- }
-
-
- /************************************************
- * function: short object_remove(void *data)
- * remove an object from the active object list
- * hide the cursor before calling this and restore it afterwards
- * parameters: pointer to data of object (this is essentially the "handle")
- * returns: 1 if OK or 0 if FAIL
- ************************************************/
- short object_remove(void *data)
- {
- GOB *o,*o2;
-
- o = first_object.next;
- o2 = &first_object;
- while (o != NULL) /* scan entire object list */
- {
- if (data == o->data) /* for object of interest */
- {
- o2->next = o->next; /* unlink it */
- fg_restore(o->save_area); /* restore background */
- free(o); /* clear it */
- return 1;
- }
- o2 = o;
- o = o->next;
- }
- return 0;
- }
-
-
- /************************************************
- * function: GOB *object_exists(void *data)
- * parameters: pointer to data of an object
- * returns: pointer to object or NULL if object does not exist in chain
- ************************************************/
- GOB *object_exists(void *data)
- {
- GOB *o;
-
- for (o = first_object.next ; o != NULL ; o = o->next)
- if (data == o->data)
- break;
- return o;
- }
-
-
- /************************************************
- * function: short message_get(MESSAGE *message)
- * parameters: pointer to MESSAGE struct
- * returns: message id
- ************************************************/
- short message_get(MESSAGE *message)
- {
- short c;
- unsigned short mouse_state;
- short x,y;
-
- for (;;)
- {
- if (message_queue_tail != message_queue_head) /* check message queue */
- {
- *message = message_queue[message_queue_head];
- if (++message_queue_head >= MESSAGE_QUEUE_SIZE)
- message_queue_head = 0;
- if (scan_object_list(message))
- break;
- }
- if ((c = checkey()) != -1) /* check keyboard */
- {
- message->data.short_data.x = c;
- message->id = M_KEY;
- if (scan_object_list(message))
- break;
- }
- if (fg.msm) /* check mouse */
- {
- mouse_state = fg_msm_getstatus((fg_coord_t *)&x,(fg_coord_t *)&y);
- if (mouse_state != last_mouse_state) /* if buttons changed */
- {
- c = last_mouse_state;
- last_mouse_state = mouse_state; /* update last state */
- last_mouse_x = x;
- last_mouse_y = y;
- if (c != 0)
- continue; /* button went up or playing chords */
- if (mouse_state & FG_MSM_MIDDLE)
- message->id = M_MOUSE_CENTER;
- if (mouse_state & FG_MSM_RIGHT)
- message->id = M_MOUSE_RIGHT;
- if (mouse_state & FG_MSM_LEFT)
- message->id = M_MOUSE_LEFT;
- message->data.short_data.x = x; /* update message coordinates */
- message->data.short_data.y = y;
- if (scan_object_list(message))
- break;
- }
- }
- }
- return message->id;
- }
-
-
- /************************************************
- * function: void message_send(MESSAGE *message)
- * queue up a message for message_get
- * parameters: pointer to MESSAGE struct
- * returns: nothing
- ************************************************/
- void message_send(MESSAGE *message)
- {
- short old_tail;
-
- old_tail = message_queue_tail;
- message_queue[message_queue_tail] = *message;
- if (++message_queue_tail >= MESSAGE_QUEUE_SIZE)
- message_queue_tail = 0;
- if (message_queue_tail == message_queue_head)
- {
- message_queue_tail = old_tail;
- message_queue[message_queue_tail].id = gui_errno = M_MESSAGE_OVERFLOW;
- message_queue[message_queue_tail].data.ptr_data = message_send;
- }
- }
-
-
- /************************************************
- * function: short message_send_object(MESSAGE *message,void *data)
- * send a message to a single object
- * parameters: pointer to a message and pointer to data for object (used as a handle)
- * returns: 1 if sent OK or 0 if not in active object queue
- ************************************************/
- short message_send_object(MESSAGE *message,void *data)
- {
- GOB *o;
-
- if ((o = object_exists(data)) == NULL)
- return 0;
- (*(o->message_handler))(message,o->data);
- return 1;
- }
-
-
- /************************************************
- * function: short message_box(char *str)
- * puts a message on the screen and waits for a key or mouse click
- * parameters: string for message
- * returns: 0 if failure (string too long or non memory), else 1 if OK
- ************************************************/
- short message_box(char *str)
- {
- return message_box_core(str,0);
- }
-
-
- /************************************************
- * function: short error_box(short id)
- * displays an error box with appropriate message if message is in the range of error
- * parameters: id of error message
- * returns: 1 if displayed or 0 if not
- ************************************************/
- short error_box(short id)
- {
- char *p;
-
- if (id >= M_MIN_ERROR && id <= M_MAX_ERROR)
- {
- switch (id)
- {
- case M_NOMEM:
- p = "Out of memory";
- break;
- case M_MESSAGE_OVERFLOW:
- p = "Message queue overflow, messages lost";
- break;
- case M_INVALID_PARMS:
- p = "A procedure received invalid parameters";
- break;
- case M_NOT_OPEN:
- p = "Attempt to access an unopened object";
- break;
- default:
- p = "Unknown error";
- break;
- }
- gui_errno = 0;
- return message_box(p);
- }
- return 0;
- }
-
-
-
- /************************************************
- * function: short yn_box(char *str)
- * puts a message on the screen and waits for a yes/no response
- * note: the string " Y/N?" is appended to the message
- * parameters: string for message
- * returns: 1 if Yes, 0 if No or failed
- ************************************************/
- short yn_box(char *str)
- {
- char local_str[MESSAGE_MAX_STR+1];
- static char yn_str[] = MESSAGE_YN;
-
- strncpy(local_str,str,MESSAGE_MAX_STR-sizeof(yn_str));
- local_str[MESSAGE_MAX_STR-sizeof(yn_str)] = 0;
- strcat(local_str,yn_str);
- return message_box_core(local_str,1);
- }
-
-
- /************************************************
- * function: short exclusive_focus_set(void *data)
- * sets exclusive focus to an object, any object setting the
- * focus has the responsibility of clearing it. The mouse is
- * restricted to the dimensions of the object
- * NOTE: the cursor should be turned off before calling this function
- * parameters: pointer to an object's data, used as a handle
- * returns: 1 if set or 0 if no such object in active list or stack is full
- ************************************************/
- short exclusive_focus_set(void *data)
- {
- GOB *o;
-
- if (exclusive_focus_count >= EXCLUSIVE_FOCUS_STACK_SIZE)
- return 0;
- if ((o = object_exists(data)) == NULL)
- return 0;
- exclusive_focus[exclusive_focus_count++] = o;
- fg_msm_setarea(o->screen); /* restrict mouse to object */
- fg_msm_setcurpos(fg_coord_midpoint(o->screen[FG_X1],o->screen[FG_X2]),fg_coord_midpoint(o->screen[FG_Y1],o->screen[FG_Y2]));
- return 1;
- }
-
-
- /************************************************
- * function: short exclusive_focus_clear(void *data)
- * clears the exclusive focus of an object and restores the focus of the previous object
- * NOTE: the cursor should be turned off before calling this function
- * parameters: pointer to an object's data, used as a handle
- * returns: 1 if cleared or 0 if not
- ************************************************/
- short exclusive_focus_clear(void *data)
- {
- GOB *o;
- short i;
-
- if ((o = object_exists(data)) == NULL)
- return 0;
- for (i = 0 ; i < exclusive_focus_count ; i++)
- if (exclusive_focus[i] == o)
- {
- exclusive_focus_count = i;
- if (exclusive_focus_count == 0) /* allow mouse to roam previous object */
- fg_msm_setarea(fg.displaybox);
- else
- fg_msm_setarea(exclusive_focus[i-1]->screen);
- return 1;
- }
- return 0;
- }
-
-
- /************************************************
- * function: void input_handler_set_default(void (*message_handler))
- * sets up a default message handler for mouse or key events. This handler
- * is automatically cleared everytime the object list is scanned with an event.
- * This means that it is a one shot function that can be set by the message handler
- * of an object.
- * parameters: pointer to function for handling default messages
- * returns: nothing
- ************************************************/
- void input_handler_set_default(void (*message_handler)(MESSAGE *message))
- {
- default_input_handler = message_handler;
- }
-
-
- /************************************************
- * function: void screen_clear(void)
- * clears the screen to the SYSTEM_BACKGROUND color
- * parameters: none
- * returns: none
- ************************************************/
- void screen_clear(void)
- {
- fg_msm_hidecursor();
- fg_fillbox(COLOR_SYSTEM_BACKGROUND,FG_MODE_SET,~0,fg.displaybox);
- fg_msm_showcursor();
- fg_flush();
- }
-
-
- /* ---------------- LOCAL FUNCTIONS ---------------- */
-
- /* scan object list with a message, returns 1 if message available or 0 if M_NONE */
- static short scan_object_list(MESSAGE *message)
- {
- GOB *o;
-
- for (o = first_object.next ; o != NULL ; o = o->next)
- { /* for each object */
- if (exclusive_focus_count > 0 && o != exclusive_focus[exclusive_focus_count-1])
- continue;
- (*(o->message_handler))(message,o->data);
- if (message->id == M_NONE)
- return 0;
- }
- if (default_input_handler != NULL)
- {
- if (message->id >= M_MIN_INPUT && message->id <= M_MAX_INPUT)
- (*default_input_handler)(message);
- default_input_handler = NULL;
- }
- return 1;
- }
-
-
- /* waits for a keypress and returns the ASCII value */
- /* non-ASCII keys return 256+scan code */
- static short getkey(void)
- {
- short c;
-
- while ((c = checkey()) == -1)
- ;
- return c;
- }
-
-
- /* checks if key is ready and returns it if so, else returns -1 */
- /* non-ASCII keys return 256+scan code */
- /* MSDOS dependent */
- static short checkey(void)
- {
- unsigned short c;
-
- if (_bios_keybrd(_KEYBRD_READY) == 0)
- return -1;
- c = _bios_keybrd(_KEYBRD_READ);
- if ((c & 0xff) == 0)
- {
- c = ((c & 0xff00) >> 8);
- if (c == 3)
- c = 0;
- else
- c += 256;
- }
- else
- c &= 0xff;
- return (short) c;
- }
-
-
- /* wait until the user presses a key or clicks the mouse, returns keypress or -1 if mouse click, mouse values in x,y */
- static short wait_key_or_mouse_click(short *x,short *y)
- {
- short c,tx,ty;
-
- while (fg_msm_getstatus((fg_coord_t *)&tx,(fg_coord_t *)&ty))
- ;
- while ((c = checkey()) == -1)
- {
- if (fg_msm_getstatus((fg_coord_t *)x,(fg_coord_t *)y))
- {
- while (fg_msm_getstatus((fg_coord_t *)&tx,(fg_coord_t *)&ty))
- ;
- return -1;
- }
- }
- return c;
- }
-
-
- /* handler for message box */
- static void message_box_handler(MESSAGE *message,void *data)
- {
- message->id = M_NONE;
- }
-
-
- /* display message and wait for keystroke or mouse click, if yn=1 then waits for yes/no response */
- static short message_box_core(char *str,short yn)
- {
- unsigned short dx,dy;
- fg_box_t message_area,box,box2;
- void *temp_data;
- short ret,c,x,y;
-
- temp_data = &temp_data; /* nothing is assigned to *temp_data, just a handle */
- dx = (strlen(str) + 2) * gui_char_width;
- dy = 2 * gui_char_height;
- if (dx > gui_screen_width || dy > gui_screen_height)
- return 0;
- message_area[FG_X1] = (fg.displaybox[FG_X2] - fg.displaybox[FG_X1])/2 - dx/2 + fg.displaybox[FG_X1];
- message_area[FG_X2] = message_area[FG_X1] + dx;
- message_area[FG_Y1] = (fg.displaybox[FG_Y2] - fg.displaybox[FG_Y1])/2 - dy/2 + fg.displaybox[FG_Y1];
- message_area[FG_Y2] = message_area[FG_Y1] + dy;
- fg_boxclip(fg.displaybox,message_area,message_area);
- fg_msm_hidecursor();
- if (!object_add(OBJECT_MESSAGEBOX,message_box_handler,temp_data,message_area))
- {
- fg_msm_showcursor();
- fg_flush();
- return 0;
- }
- fg_fillbox(COLOR_MESSAGE_BACKGROUND,FG_MODE_SET,~0,message_area);
- fg_drawbox(COLOR_MESSAGE_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,message_area,fg.displaybox);
- box[FG_X1] = message_area[FG_X1] + 2;
- box[FG_X2] = message_area[FG_X2] - 2;
- box[FG_Y1] = message_area[FG_Y1] + 2;
- box[FG_Y2] = message_area[FG_Y2] - 2;
- fg_drawbox(COLOR_MESSAGE_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,box,fg.displaybox);
- fg_puts(COLOR_MESSAGE_FOREGROUND,FG_MODE_SET,~0,FG_ROT0,message_area[FG_X1]+gui_char_width,message_area[FG_Y1]+gui_char_height/2,str,fg.displaybox);
- if (yn)
- {
- fg_box_cpy(box2,box);
- dx = gui_char_width * strlen(MESSAGE_YN);
- box[FG_X1] = message_area[FG_X2] - dx - gui_char_width;
- box[FG_X2] = box[FG_X1] + dx/2;
- box2[FG_X1] = box[FG_X2] + 1;
- box2[FG_X2] = box2[FG_X1] + dx/2;
- exclusive_focus_set(temp_data);
- fg_msm_setcurpos(box[FG_X1]+gui_char_width+gui_char_width/2,box[FG_Y1]+gui_char_height/2);
- }
- fg_msm_showcursor();
- fg_flush();
- ret = 1;
- if (yn)
- {
- for (;;)
- {
- c = wait_key_or_mouse_click(&x,&y);
- if (tolower(c) == MESSAGE_YES || c == RETURN)
- break;
- if (tolower(c) == MESSAGE_NO)
- {
- ret = 0;
- break;
- }
- if (c == -1)
- {
- if (fg_pt_inbox(box,x,y))
- break;
- if (fg_pt_inbox(box2,x,y))
- {
- ret = 0;
- break;
- }
- }
- }
- exclusive_focus_clear(temp_data);
- }
- else
- wait_key_or_mouse_click(&x,&y);
- fg_msm_hidecursor();
- object_remove(temp_data);
- fg_msm_showcursor();
- fg_flush();
- return ret;
- }
-
-
- #ifndef DEBUG
- /* intercept critical errors, this is specific to Zortech */
- /* make sure stack checking is turned off before entering this function */
- static int _far _cdecl critical_error_handler(int *ax,int *di)
- {
- #define CRITICAL_ERROR_RETRY 1
- #define CRITICAL_ERROR_FAIL 3
- #define DISK_LETTER_OFFSET 15
- static char disk_error[] = "Error on Drive X, Retry";
- static char device_error[] = "Device Error, Retry";
- char *error;
-
- if (*ax & 0x8000)
- error = device_error;
- else
- {
- error = disk_error;
- disk_error[DISK_LETTER_OFFSET] = (*ax & 0xff) + 'A';
- }
- if (yn_box(error))
- *ax = CRITICAL_ERROR_RETRY;
- else
- *ax = CRITICAL_ERROR_FAIL;
- return *ax;
- }
- #endif